package org.xx.armory.db.impl;

import org.xx.armory.db.DbException;
import org.xx.armory.db.Transaction;
import org.xx.armory.db.TransactionManager;
import org.xx.armory.db.TransactionProperties;

import java.util.Objects;

import static org.xx.armory.commons.Validators.notNull;

/**
 * 使用线程作为上下文的事务管理器。
 * <p>默认不允许自动提交。</p>
 */
public final class ThreadContextTransactionManager
        extends AbstractTransactionManager
        implements TransactionManager, AutoCloseable {
    private final ThreadLocal<TransactionProperties> transactionPropertiesRef;
    private final ThreadLocal<SharableTransaction> transactionRef;

    /**
     * 构造ThreadContextTransactionManager实例。
     */
    public ThreadContextTransactionManager() {
        this.transactionPropertiesRef = ThreadLocal.withInitial(TransactionProperties::new);
        this.transactionRef = new ThreadLocal<>();
    }

    /**
     * 获取事务的属性。
     *
     * @return 事务的属性。
     */
    public final TransactionProperties getProperties() {
        return this.transactionPropertiesRef.get();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setProperties(
            TransactionProperties properties
    ) {
        notNull(properties, "properties");

        final TransactionProperties oldProperties = this.transactionPropertiesRef.get();
        if (!Objects.equals(properties, oldProperties)) {
            this.transactionPropertiesRef.set(properties);
            transactionPropertiesChanged(oldProperties, properties);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Transaction current()
            throws DbException {
        SharableTransaction transaction = this.transactionRef.get();
        if (transaction == null || !transaction.isActive()) {
            try {
                final SharableTransaction newTransaction = new SharableTransaction(getDataSource(), getProperties());
                this.transactionRef.set(newTransaction);
            } catch (Exception ex) {
                throw new IllegalStateException("cannot create thread context transaction", ex);
            }
        }

        return this.transactionRef.get();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void close()
            throws Exception {
        final SharableTransaction transaction = this.transactionRef.get();
        this.transactionRef.remove();
        if (transaction != null && transaction.isActive()) {
            transaction.close();
        }
    }
}
